Satellite Image Data


Analysis using numpy


Data Source: Satellite Image from WIFIRE Project

WIFIRE is an integrated system for wildfire analysis, with specific regard to changing urban dynamics and climate. The system integrates networked observations such as heterogeneous satellite data and real-time remote sensor data, with computational techniques in signal processing, visualization, modeling, and data assimilation to provide a scalable method to monitor such phenomena as weather patterns that can help predict a wildfire's rate of spread. You can read more about WIFIRE at: https://wifire.ucsd.edu/

In this example, we will analyze a sample satellite image dataset from WIFIRE using the numpy Library.

Loading the libraries we need: numpy, scipy, matplotlib

In [1]:
%matplotlib inline
import numpy as np
from scipy import misc
import matplotlib.pyplot as plt

Creating a numpy array from an image file:


Lets choose a WIFIRE satellite image file as an ndarray and display its type.

In [2]:
from skimage import data

photo_data = misc.imread('./wifire/sd-3layers.jpg')

type(photo_data)
Out[2]:
numpy.ndarray

Let's see what is in this image.

In [7]:
plt.figure(figsize=(15,15))
plt.imshow(photo_data)
Out[7]:
<matplotlib.image.AxesImage at 0x1c2c751128>

photo_data.shape

print(photo_data)

The shape of the ndarray show that it is a three layered matrix. The first two numbers here are length and width, and the third number (i.e. 3) is for three layers: Red, Green and Blue.

RGB Color Mapping in the Photo:


  • RED pixel indicates Altitude

  • BLUE pixel indicates Aspect

  • GREEN pixel indicates Slope

    </ul>
    The higher values denote higher altitude, aspect and slope.

In [5]:
photo_data.size
Out[5]:
53606475
In [6]:
photo_data.min(), photo_data.max()
Out[6]:
(0, 255)
In [12]:
photo_data.mean()
Out[12]:
58.354229913457281


Pixel on the 150th Row and 250th Column

In [13]:
photo_data[150, 250]
Out[13]:
array([ 17,  35, 255], dtype=uint8)
In [14]:
photo_data[150, 250, 1]
Out[14]:
35


Set a Pixel to All Zeros


We can set all three layer in a pixel as once by assigning zero globally to that (row,column) pairing. However, setting one pixel to zero is not noticeable.

In [15]:
#photo_data = misc.imread('./wifire/sd-3layers.jpg')
photo_data[150, 250] = 0
plt.figure(figsize=(10,10))
plt.imshow(photo_data)
Out[15]:
<matplotlib.image.AxesImage at 0x1c2c94a208>


Changing colors in a Range


We can also use a range to change the pixel values. As an example, let's set the green layer for rows 200 t0 800 to full intensity.

In [16]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')

photo_data[200:800, : ,1] = 255
plt.figure(figsize=(10,10))
plt.imshow(photo_data)
Out[16]:
<matplotlib.image.AxesImage at 0x10ea32ba8>
In [17]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')

photo_data[200:800, :] = 255
plt.figure(figsize=(10,10))
plt.imshow(photo_data)
Out[17]:
<matplotlib.image.AxesImage at 0x1c42b712b0>
In [18]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')

photo_data[200:800, :] = 0
plt.figure(figsize=(10,10))
plt.imshow(photo_data)
Out[18]:
<matplotlib.image.AxesImage at 0x1c4a466e48>


Pick all Pixels with Low Values

In [19]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')
print("Shape of photo_data:", photo_data.shape)
low_value_filter = photo_data < 200
print("Shape of low_value_filter:", low_value_filter.shape)
Shape of photo_data: (3725, 4797, 3)
Shape of low_value_filter: (3725, 4797, 3)

Filtering Out Low Values


Whenever the low_value_filter is True, set value to 0.

In [20]:
#import random
plt.figure(figsize=(10,10))
plt.imshow(photo_data)
photo_data[low_value_filter] = 0
plt.figure(figsize=(10,10))
plt.imshow(photo_data)
Out[20]:
<matplotlib.image.AxesImage at 0x1c4a3fe048>

More Row and Column Operations


You can design complex patters by making cols a function of rows or vice-versa. Here we try a linear relationship between rows and columns.

In [21]:
rows_range = np.arange(len(photo_data))
cols_range = rows_range
print(type(rows_range))
<class 'numpy.ndarray'>
In [22]:
photo_data[rows_range, cols_range] = 255
In [23]:
plt.figure(figsize=(15,15))
plt.imshow(photo_data)
Out[23]:
<matplotlib.image.AxesImage at 0x11ac4bef0>


Masking Images


Now let us try something even cooler...a mask that is in shape of a circular disc.

In [24]:
total_rows, total_cols, total_layers = photo_data.shape
#print("photo_data = ", photo_data.shape)

X, Y = np.ogrid[:total_rows, :total_cols]
#print("X = ", X.shape, " and Y = ", Y.shape)
In [25]:
center_row, center_col = total_rows / 2, total_cols / 2
#print("center_row = ", center_row, "AND center_col = ", center_col)
#print(X - center_row)
#print(Y - center_col)
dist_from_center = (X - center_row)**2 + (Y - center_col)**2
#print(dist_from_center)
radius = (total_rows / 2)**2
#print("Radius = ", radius)
circular_mask = (dist_from_center > radius)
#print(circular_mask)
print(circular_mask[1500:1700,2000:2200])
[[False False False ..., False False False]
 [False False False ..., False False False]
 [False False False ..., False False False]
 ..., 
 [False False False ..., False False False]
 [False False False ..., False False False]
 [False False False ..., False False False]]
In [26]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')
photo_data[circular_mask] = 0
plt.figure(figsize=(15,15))
plt.imshow(photo_data)
Out[26]:
<matplotlib.image.AxesImage at 0x11ac5e748>

Further Masking


You can further improve the mask, for example just get upper half disc.

In [27]:
X, Y = np.ogrid[:total_rows, :total_cols]
half_upper = X < center_row # this line generates a mask for all rows above the center

half_upper_mask = np.logical_and(half_upper, circular_mask)
In [28]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')
photo_data[half_upper_mask] = 255
#photo_data[half_upper_mask] = random.randint(200,255)
plt.figure(figsize=(15,15))
plt.imshow(photo_data)
Out[28]:
<matplotlib.image.AxesImage at 0x1c2c9c5940>


Further Processing of our Satellite Imagery

Processing of RED Pixels

Remember that red pixels tell us about the height. Let us try to highlight all the high altitude areas. We will do this by detecting high intensity RED Pixels and muting down other areas.

In [29]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')
red_mask   = photo_data[:, : ,0] < 150

photo_data[red_mask] = 0
plt.figure(figsize=(15,15))
plt.imshow(photo_data)
Out[29]:
<matplotlib.image.AxesImage at 0x1c6c51f278>


Detecting Highl-GREEN Pixels

In [30]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')
green_mask = photo_data[:, : ,1] < 150

photo_data[green_mask] = 0
plt.figure(figsize=(15,15))
plt.imshow(photo_data)
Out[30]:
<matplotlib.image.AxesImage at 0x1c6c5a49b0>


Detecting Highly-BLUE Pixels

In [31]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')
blue_mask  = photo_data[:, : ,2] < 150

photo_data[blue_mask] = 0
plt.figure(figsize=(15,15))
plt.imshow(photo_data)
Out[31]:
<matplotlib.image.AxesImage at 0x11ab87198>


Composite mask that takes thresholds on all three layers: RED, GREEN, BLUE

In [32]:
photo_data = misc.imread('./wifire/sd-3layers.jpg')

red_mask   = photo_data[:, : ,0] < 150
green_mask = photo_data[:, : ,1] > 100
blue_mask  = photo_data[:, : ,2] < 100

final_mask = np.logical_and(red_mask, green_mask, blue_mask)
photo_data[final_mask] = 0
plt.figure(figsize=(15,15))
plt.imshow(photo_data)
Out[32]:
<matplotlib.image.AxesImage at 0x1c6c4a2630>